/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is lutsko.com code.
 *
 * The Initial Developer of the Original Code is 
 * James Lutsko.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package autoteam;


import java.io.*;
import java.util.*;


/**
 * The Drafter class implements the algorithm
 * for drafting a team.
 */



public class Drafter implements Serializable {


    public Drafter(Skill _dataMap) {dataMap = _dataMap;}

    public int getNumberUnassigned(){ return pool.size();}

    public void recordUnassigned(String msg)
    {
	for(Iterator I = pool.iterator();I.hasNext();)
	    {
		Child c= (Child) I.next();
		msg += "\t" + c.firstName + " " + c.lastName + "\n";
	    }
    }

    public Child getUnassigned(int pos)
    {
	if(pool.size() > pos) return (Child) pool.elementAt(pos);
	return null;
    }

    void initializeDraft(Vector _kids, Vector _carPools, Vector _sibs, Vector teams, boolean _doCarPool)
    {
	pool = new Vector(_kids);
	carPools = new Vector(_carPools);
	sibs = new Vector(_sibs);
	doCarPool = _doCarPool;

	//1. Initialize children and teams
	for(Iterator I = pool.iterator();I.hasNext();)
	    {	       		
		Child c = (Child) I.next();
		c.initialize();
	    }
	
	for(Iterator I = teams.iterator();I.hasNext();)
	    {
		Team t = (Team) I.next();
		t.initialize(dataMap.getNumberOfSkills());
	    }
	maxSize = pool.size();
	if(teams.size() > 0) maxSize /= teams.size();
	if(maxSize*teams.size() < pool.size()) maxSize++;
    }

    void doDraft( Vector teams, League league )
    {	
	league .appendToLog("\n------------------------Performing Draft----------------------------");
	league .appendToLog("Date: " + (new Date()));

	league .appendToLog("Draft type is " + (draftType == 0 ? "Simple" : "Positional"));

	if(draftType == 0 && hCriteria != null)
	    {
		league .appendToLog("Tie Breakers in order: ");
		    
		if(hCriteria.size() > 0)
		    {
			for(Iterator I = hCriteria.iterator();I.hasNext();)
			    {
				DraftCriterion dc = (DraftCriterion) I.next();
				league .appendToLog("\t" + dataMap.getSkillName(dc.getSkill()));
			    }
		    }
	    } else if(pCriteria != null) {
		league .appendToLog("Criteria are: ");
		for(Iterator I = pCriteria.iterator();I.hasNext();)
		    {
			DraftCriterion dc = (DraftCriterion) I.next();
			league .appendToLog("\t" + dataMap.getSkillName(dc.getSkill())
					+ " for " + dc.Parameter + " rounds"
					+ " (Tie Breaker: " + dataMap.getSkillName(dc.getTiebreaker()) + ")");
		    }
	    }

	// record the sibs and carpools
	league .appendToLog("Siblings: ");
	for(Iterator I = sibs.iterator();I.hasNext();)
	    {
		ChildPair cp = (ChildPair) I.next();
		Child c1 = (Child) cp.child1;
		Child c2 = (Child) cp.child2;
		league .appendToLog("\t " + c1 + " is with " + c2);
	     }  

	league .appendToLog("Carpools: ");
	for(Iterator I = carPools.iterator();I.hasNext();)
	    {
		ChildPair cp = (ChildPair) I.next();
		Child c1 = (Child) cp.child1;
		Child c2 = (Child) cp.child2;
		league .appendToLog("\t " + c1 + " is with " + c2 + " and carpool is " + (cp.active ? "active" : "inactive"));
	     }  

	//2.0 Pre assign kids
	league .appendToLog("Coaches: ");
	for(Iterator I = teams.iterator();I.hasNext();)
	    {
		Team t = (Team) I.next();
		Child c = t.coachKid;
		if(c == null) continue;
		c.setTeam(t);
		league .appendToLog("\t"+ c.toString() + " assigned to " + t.name + " (coach's kid)");
		processLinks(t,league );
	    }


	// 3.0 Now comes the actual drafting

	boolean stop = false;
	for(int round = 1;  !stop && pool.size() > 0; round++)
	    {

		doSort(round,league );

		stop = true;

		// 3.1 rank & sort teams
		Collections.sort(teams);
		Collections.reverse(teams); // weakest goes first

		// 3.2 each team picks the next best kid
		for(Iterator I = teams.iterator();I.hasNext();)
		    {
			Team t = (Team) I.next();
			// Find the kid: either he is already assigned, or we locate a candidate
			Child theKid = null;

			// Is there an attached kid in the next N?
			int count = teams.size();
			for(Iterator I1 = pool.iterator();
			    I1.hasNext() && theKid == null && count > 0;)
			    {	       		
				Child c = (Child) I1.next();
				
				// assigned child - take him
				if(c.isTeam(t)) {theKid = c; break;}
				count--;
			    }
			//			if(t.getAssigned() >= maxSize) 
			//			    {
			//				doc.appendToLog("Skipping " + t.name + " (too many players)\n");
			//				continue;
			//			    }


			if(theKid == null)
			    for(Iterator I1 = pool.iterator();
				I1.hasNext() && theKid == null;)
				{	       		
				    Child c = (Child) I1.next();
				    
				    // assigned child - take him (should not happen)
				    if(c.isTeam(t)) {theKid = c; break;}
				    
				    // assinged to some other team - skip him
				    if(c.hasTeam() && !c.isTeam(t)) continue;
				    
				    if(theKid == null)
					if(c.checkNight(t))
					    theKid = c;
				}
			// if we got one - add him to the team
			if(theKid != null)
			    {
				league .appendToLog("    Team = " + t.name + "\n"
						+ "        Total Skill = "
						+ Local.format((Virtue.currentSkill < 0 ? t.totalSkill
								: t.skill[Virtue.currentSkill]
										.doubleValue())) + ", Size = "
						+ t.children.size());
				theKid.setTeam(t);
				t.addChild(theKid);
				pool.remove(theKid);
				league .appendToLog("        Added " + theKid.toString() + " ("
						+ Local.format(Virtue.currentSkill < 0 ? theKid.totalSkill
								: theKid.skill[Virtue.currentSkill]
										.doubleValue()) + ")");
				league .appendToLog("        New Total Skill = "
						+ Local.format((Virtue.currentSkill < 0 ? t.totalSkill
								: t.skill[Virtue.currentSkill]
										.doubleValue())) + ", New Size = "
						+ t.children.size());
				
				processLinks(t,league );
				stop = false;
			    }
		    }
	    }
    }
    
    void unassigned( Team team, League league )
    {	
	team.removeAllChildren();
	team.initialize(dataMap.getNumberOfSkills());
	for(Iterator I = pool.iterator(); I.hasNext();)
	    {
		Child c = (Child) I.next();
		team.addChild(c);
		league.appendToLog("Unassigned: " + c.toString());
	    }
    }

    void processLinks(Team t, League doc)
    {
	boolean stop = false;
	while(!stop)
	    {
		stop = true;
		for(Iterator I =carPools.iterator();I.hasNext() && stop && doCarPool;)
		    {
			ChildPair cp = (ChildPair) I.next();
			if(!cp.active) continue;
			Child c1 = (Child) cp.child1;
			Child c2 = (Child) cp.child2;
			if(c1 == null || c2 == null) continue;

			if(c1.hasTeam()) {c2.setTeam(c1.getTeam()); stop = false; carPools.remove(cp);}
			else if(c2.hasTeam()) {c1.setTeam(c2.getTeam()); stop = false; carPools.remove(cp);}
			else continue;
			doc.appendToLog("\tCarpooling  " + c1 + " and " + c2 + " : team is " + c1.getTeam().name);
		    }
		for(Iterator I =sibs.iterator();I.hasNext() && stop;)
		    {
			ChildPair cp = (ChildPair) I.next();
			Child c1 = (Child) cp.child1;
			Child c2 = (Child) cp.child2;

			if(c1.hasTeam()) {c2.setTeam(c1.getTeam()); stop = false; sibs.remove(cp);}
			else if(c2.hasTeam()) {c1.setTeam(c2.getTeam()); stop = false; sibs.remove(cp);}
			else continue;

			doc.appendToLog("\tSibs  " + c1 + " and " + c2 + " : team is " + c1.getTeam().name);	
		    }
	    }
    }

    void doSort( int round, League league )
    {
	int actualRound = round;
	if(draftType == 0 && hCriteria != null)
	    {
		// pack
		while(hCriteria.remove(null)){;}
		Virtue.skillOrder = null;
		if (hCriteria.size() > 0) {
			Virtue.skillOrder = new int[hCriteria.size() + 1];
			int pos = 0;
			Virtue.skillOrder[pos] = -1;
			pos++;
			for (Iterator i = hCriteria.iterator(); i.hasNext(); pos++)
				Virtue.skillOrder[pos] = ((DraftCriterion) i.next()).getSkill();
		}
	    } else {
		// which skill for this round?
		// pack
		while(pCriteria.remove(null)){;}
		int sum = 0;
		DraftCriterion dc = null;
		for(Iterator I = pCriteria.iterator();I.hasNext();)
		    {
			dc = (DraftCriterion) I.next();
			if(round <= dc.Parameter.intValue()) break;
			round -= dc.Parameter.intValue();
		    }
		
		Virtue.skillOrder = new int[1];		
		Virtue.skillOrder[0] = dc.getSkill();
		
		if (pCriteria.size() > 0) {
			Virtue.tieBreakers = new int[pCriteria.size()];
			int pos = 0;
			for (Iterator i = pCriteria.iterator(); i.hasNext(); pos++)
				Virtue.tieBreakers[pos] = ((DraftCriterion) i.next()).getTiebreaker();
		}
	    }

	if (league.getDraftType() == 0)
		league.appendToLog("Round " + actualRound);
	else
	{
		if (Virtue.skillOrder != null) {
			league.appendToLog("Round " + actualRound + " (Using skill: "
						+ dataMap.getSkillName(Virtue.skillOrder[0]) + ")");
		}
		else
			league.appendToLog("Round " + actualRound + " (No skill specified for this round)");
	}
	Collections.sort(pool);
    }




    /**
     * Indicates what type of draft we are doing.
     * Current values are:
     *    0 = hierarchical
     *    1 = positional
     */

    public int draftType = 0;

    /**
     * A vector holding the criteria for a positional draft.
     */
    Vector pCriteria = null;

    /**
     * A vector holding the criteria for a hierarchical draft.
     */
    Vector hCriteria = null;


    /**
     * return the appropriate criteria object
     */
    public Vector getCriteria()
    {
	if(draftType == 0)
	    {
		if(hCriteria == null) hCriteria = new Vector();
		return hCriteria;
	    } else if(draftType == 1) {
		if(pCriteria == null) pCriteria = new Vector();
		return pCriteria;
	    }
	return null;
    }


    Skill dataMap = null;
    private Vector pool = null;
    private Vector carPools = null;
    private Vector sibs = null;
    private int maxSize = 0;
    private boolean doCarPool = false;
    static final long serialVersionUID = -3779840418701807924L;
};
